#ifndef __MQ_BINDING_
#define __MQ_BINDING_

#include "../mqcommon/mq_log.hpp"
#include "../mqcommon/mq_tools.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <string>
#include <mutex>
#include <unordered_map>
#include <memory>

namespace MQ
{
    struct Binding
    {
        using ptr = std::shared_ptr<Binding>;

        std::string _exchange_name;
        std::string _queue_name;
        std::string _binding_key;

        Binding()
        {
        }

        Binding(const std::string &ename, const std::string &qname, const std::string &bkey)
            : _exchange_name(ename), _queue_name(qname), _binding_key(bkey)
        {
        }
    };
    // queue_name:所有的绑定
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    // 所有的绑定信息，exchangename:bindingmap
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;

    // 联合管理的优势 ->清晰一个exchange绑定了多少队列
    // 以下操作是O(1)的
    // | 场景                | 查询方式                                   |
    // | ----------------- | -------------------------------------- |
    // | 获取某个交换机下所有绑定的队列   | _bindings[exchange_name]             |
    // | 判断某个交换机下是否绑定了某个队列 | _bindings[exchange_name][queue_name] |
    // | 删除某个交换机所有绑定       | _bindings.erase(exchange_name)       |
    // | 删除某个队列在所有交换机中的绑定  | 遍历所有 _bindings[x].erase(queue_name)  |

    class BindingMapper
    {
    public:
        BindingMapper(const std::string &dbfile)
            : _sql_helper(dbfile)
        {
            std::string path = FileHelper::parentDir(dbfile);
            FileHelper::createDirectory(path);
            _sql_helper.open();
            createTable();
        }

        void delete_table()
        {
            std::string sql = "drop table if exists binding_table;";
            _sql_helper.exec(sql, nullptr, nullptr);
        }

        void delete_queue_exchange(const std::string &ename, const std::string &qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchange_name='" << ename << "'and queue_name='" << qname << "'";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        void delete_exchange_binding(const std::string &ename)
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchange_name='" << ename << "'";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        void delete_queue_binding(const std::string &qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where queue_name='" << qname << "'";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        bool insert_binding(Binding::ptr &bp)
        {
            std::stringstream ss;
            ss << "insert into binding_table values(";
            ss << "'" << bp->_exchange_name << "', ";
            ss << "'" << bp->_queue_name << "', ";
            ss << "'" << bp->_binding_key << "');";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        BindingMap recovery()
        {
            BindingMap bp;
            std::string sql = "select * from binding_table;";
            _sql_helper.exec(sql, selectCallback, &bp);
            return bp;
        }

    private:
        void createTable()
        {
            std::string sql = "create table if not exists binding_table (exchange_name varchar(32), queue_name varchar(32), binding_key varchar(128));";
            assert(_sql_helper.exec(sql, nullptr, nullptr));
        }

        static int selectCallback(void *args, int numcol, char **row, char **fields)
        {
            BindingMap *result = static_cast<BindingMap *>(args);
            Binding::ptr bp = std::make_shared<Binding>(row[0], row[1], row[2]);
            MsgQueueBindingMap &mbp = (*result)[bp->_exchange_name]; // 没有就创建
            mbp[bp->_queue_name] = bp;
            return 0;
        }

        /*
        unordered_map<std::string, std::string> um;
        string &str = um["hello"];
        str = "world";
        auto it = um.begin();
        cout << it->first << " " << it->second;
        */

    private:
        SqliteHelper _sql_helper;
    };

    /*
    BindingMap
    └── "exchange1"
        ├── "queueA" => Binding("exchange1", "queueA", "news.music.#")
        └── "queueB" => Binding("exchange1", "queueB", "news.sport.#")
    └── "exchange2"
        └── "queueX" => Binding("exchange2", "queueX", "system.error.*")
    */

    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;

        BindingManager(const std::string &dbfile)
            : _mapper(dbfile)
        {
            _bindings = _mapper.recovery();
        }

        bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            // 如果已经存在，直接返回
            auto it = _bindings.find(ename);
            if (it != _bindings.end() && it->second.find(qname) != it->second.end())
                return true;

            // 判断是否需要持久化
            Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
            if (durable)
            {
                bool ret = _mapper.insert_binding(bp);
                if (ret == false)
                {
                    LOG(ERROR, "insert binding error!!!\n");
                    return false;
                }
            }

            MsgQueueBindingMap &mbp = _bindings[ename];
            mbp.insert(std::make_pair(qname, bp));
            return true;
        }

        void unbind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return;
            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return;

            _mapper.delete_queue_exchange(ename, qname);
            _bindings[ename].erase(qname);
        }

        void deleteExchangebinding(const std::string &ename)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            _mapper.delete_exchange_binding(ename);
            _bindings.erase(ename);
        }

        void deleteQueuebinding(const std::string &qname)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            _mapper.delete_queue_binding(qname);
            for (auto &e : _bindings)
                e.second.erase(qname);
        }

        MsgQueueBindingMap getExchange(const std::string &ename)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            auto it = _bindings.find(ename);
            if (it == _bindings.end())
                return MsgQueueBindingMap();

            return it->second;
        }

        Binding::ptr getBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return Binding::ptr();

            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return Binding::ptr();

            return qit->second;
        }

        bool exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return false;
            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return false;
            return true;
        }

        size_t size()
        {
            size_t total_size = 0;
            std::unique_lock<std::mutex> lock(_mutex);
            for (auto start = _bindings.begin(); start != _bindings.end(); ++start)
                // 遍历每个交换机的绑定信息，从中移除指定队列的相关信息
                total_size += start->second.size();

            return total_size;
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.delete_table();
            _bindings.clear();
        }

    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };

}
#endif